/*
 * Copyright (c) 2001-2009, TIBCO Software Inc.
 * Use, modification, and distribution subject to terms of license.
 */

/**
 * Define package for search extension.
 */
jsx3.lang.Package.definePackage("jsx3.extension.browser", function(Search, Search_prototype) {
});
/**
 * Quick search extension for file broswer.
 */
jsx3.Class.defineClass("jsx3.extension.browser.Search", null, [jsx3.util.EventDispatcher], function(Search, Search_prototype) {
  
  var Event = jsx3.gui.Event;
  var Interactive = jsx3.gui.Interactive;
  var ToolbarButton = jsx3.gui.ToolbarButton;

  Search.Logger = jsx3.util.Logger.getLogger(Search.jsxclass.getName());
  Search.DEFAULT_VALUE = 'quick search...';
  Search.POINTCUT = 'jsx3.extension.browser.Search.POINTCUT_SHOWFILEDIALOG';
  Search.ON_SEARCH = 'jsx3.extension.browser.Search.ON_SEARCH';

  /**
   * Create a AOP pointcut to inject searchbox into browser dialog. 
   */
  Search.doAOP = function() {
    Search.Logger.trace('doAOP ... ');
    try {
      jsx3.lang.AOP.pc(Search.POINTCUT, {classes: "jsx3.io.FileDialog", methods: "showFileDialog"});
      jsx3.lang.AOP.before(Search.POINTCUT, function(intType, objParam) {
        this.onAfterPaint = function(objGUI){
          Search.Logger.trace('onAfterPaint objGUI: ' + objGUI);
          jsx3.sleep(jsx3.$F(function(){
            var settings = jsx3.ide.getIDESettings();
            var _state = settings.get('_JSX_FILE_BROWSER_SEARCH', '_SEARCH_BUTTON_STATE');
            Search.Logger.trace('onAfterPaint check IDE setting of _SEARCH_BUTTON_STATE: ' + _state);
            if (!_state || parseInt(_state) !== 1) return;
            var b = this.getDescendantOfName('jsxbutton_search');
            b._SEARCH_BUTTON_STATE = 1;
            b.setState(ToolbarButton.STATEON, true);
            ('after showFileDialog jsxbutton_search: ' + b + ', file_list: ' + 
                                this.getDescendantOfName('file_list').getRendered());
            this._search_instance._injectSearchBox({target: b});
          }).bind(this));
        };
        /* perhaps a bug, this method does not invoked */
        // this.getServer().getRootBlock().onDestroy = function(){
          // Search.Logger.trace('remove AOP pointcut: ' + Search.POINTCUT); 
          // jsx3.lang.AOP.pcrem(Search.POINTCUT);
          // this.jsxsuper();
        // };
        if(!this._search_instance)
          this._search_instance = new jsx3.extension.browser.Search(this);
      });
    } catch(e) {
      throw new jsx3.Exception("Error occurred while creating AOP pointcut... " + jsx3.NativeError.wrap(e));
    }
  };
  Search.doAOP();

  Search_prototype.init = function(fileDialog) {
    this._dialog = fileDialog;
    this._paint();
  };

  /* get amp engine. */
  Search_prototype.getEngine = function() {
    return jsx3.amp.Engine.getEngine(jsx3.IDE);
  };

  /* get search extension plugin. */
  Search_prototype.getPlugIn = function() {
    return this.getEngine().getPlugIn('jsx3.extension.browser.search');
  };

  /** 
   * Paint toolbar button and search textbox in the file dialog.
   * @private 
   * @jsxobf-clobber 
   */
  Search_prototype._paint = function() {
    var jsxwindowbar = this._dialog.getDescendantOfName('jsxwindowbar');
    Search.Logger.trace('jsxwindowbar: ' + jsxwindowbar);

    /* create search active button */
    var b = new ToolbarButton('jsxbutton_search');
    b.setImage("jsxapp:/images/icon_4.gif");
    b.setTip("quick search file or folder name ...");
    b.setType(ToolbarButton.TYPECHECK);
    b.setState(ToolbarButton.STATEOFF);

    b.subscribe(Interactive.EXECUTE, this, function(objEvent){
      b._SEARCH_BUTTON_STATE = b._SEARCH_BUTTON_STATE == 1 ? 0 : 1;
      var settings = jsx3.ide.getIDESettings();
      settings.set('_JSX_FILE_BROWSER_SEARCH', '_SEARCH_BUTTON_STATE', b._SEARCH_BUTTON_STATE);
      Search.Logger.trace('[this: ' + this + ']_SEARCH_BUTTON_STATE: ' + b._SEARCH_BUTTON_STATE);
    });
    b.subscribe(Interactive.CHANGE, this, this._injectSearchBox);

    /* catch event from file browser dialog */
    var _dialog = this._dialog;
    _dialog.subscribe(Interactive.JSXKEYDOWN, function(objEvent){
      var _event = objEvent.context.objEVENT;
      if (_event.escapeKey()) {
        _dialog.doGoUp();
      }
    });

    jsxwindowbar.setChild(b);
    jsxwindowbar.repaint();
    jsxwindowbar.getParent().setCols("*,128", true);
  };

  /** 
   * Inject the quick search box in the file name column.
   * @private 
   * @jsxobf-clobber 
   */  
  Search_prototype._injectSearchBox = function(objEvent) {
    if (!this._search_box) this._paintSearchBox();
    Search.Logger.trace('[' + this + ']_injectSearchBox: ' + objEvent.target);

    var b = objEvent.target;
    Search.Logger.trace('_injectSearchBox: _SEARCH_BUTTON_STATE: ' + b._SEARCH_BUTTON_STATE);

    if (b.instanceOf(ToolbarButton) && b._SEARCH_BUTTON_STATE == 1) {
      Search.Logger.trace('jsxbutton_search: ' + b);
      this._search_box.setDisplay(jsx3.gui.Block.DISPLAYBLOCK, true);
      jsx3.sleep(jsx3.$F(function(){
        this._search_engine = jsx3.extension.browser.SearchEngine.launch(this);
        this.getSearchEngine = function(){
          return this._search_engine;
        };
        this._search_box.focus();
        this._search_box.getRendered().select();
      }).bind(this));
    } else {
      this._search_box.setDisplay(jsx3.gui.Block.DISPLAYNONE, true);
    }
  };

  /** 
   * Paint search textbox in the file dialog.
   * @private 
   * @jsxobf-clobber 
   */  
  Search_prototype._paintSearchBox = function() {
    var file_list = this._file_list = this._dialog.getDescendantOfName('file_list');

    var clmns = file_list.getChildren();
    clmns[1].setWidth(clmns[1].getWidth() + 80);
    clmns[3].setWidth(clmns[3].getWidth() - 80);

    file_list.subscribe(Interactive.AFTER_RESIZE, this, jsx3.$F(function(objEvent){
      this._search_box.setWidth(clmns[1].getRendered().offsetWidth * 0.8, true);
    }).bind(this));

    /* create bubble to show the search result list */
    var bubble = this._paintSearchBubble();

    /* create search textbox */
    var txt = this._search_box = new jsx3.gui.TextBox('search_box');
    txt.setWidth(clmns[1].getRendered().offsetWidth * 0.8).setHeight(17); /* set width 80% of file column */
    txt.setFontSize(10).setBackgroundColor('#F6F6F6').setColor('#777777').setPadding('0 0 0 0').setZIndex(2);
    txt.setValue(Search.DEFAULT_VALUE).setDisplay(jsx3.gui.Block.DISPLAYNONE);
    txt.setRelativePosition(jsx3.gui.Block.ABSOLUTE);
    txt.setLeft(clmns[0].getRendered().offsetWidth + 33).setTop(2);
    // txt.setMenu('search_bubble');

    txt._onExecute = jsx3.$F(function(action, objEvent){
      if(typeof action == 'string' && this[action] && typeof this[action] == 'function')
        return this[action](txt, objEvent);
    }).bind(this);

    txt.setEvent('this._onExecute("_doKeyDown", objEVENT);', Interactive.JSXKEYDOWN);
    txt.setEvent('this._onExecute("_doKeyUp", objEVENT);', Interactive.JSXKEYUP);
    // txt.setEvent('this._onExecute("_doClick", objEVENT);', Interactive.JSXCLICK);
    txt.setEvent('this._onExecute("_doMouseDown", objEVENT);', Interactive.JSXMOUSEDOWN);
    // txt.setEvent('this._onExecute("_doMouseUp", objEVENT);', Interactive.JSXMOUSEUP);
    txt.setEvent('this._onExecute("_doBlur", objEVENT);', Interactive.JSXBLUR);
    // txt.setEvent('this._onExecute("_doChange", objEVENT);', Interactive.CHANGE);
    txt.setEvent('this._onExecute("_doExecute", objEVENT);', Interactive.EXECUTE);
    // txt.setEvent('this._onExecute("_checkBubble", objEVENT);', Interactive.MENU);

    var parent = file_list.getParent();
    parent.setChild(bubble);
    parent.paintChild(bubble);
    parent.setChild(txt);
    parent.paintChild(txt);
    Search.Logger.trace('[' + this + ']_paintTextbox :: search_box: ' + this._search_box);
  };

  /** 
   * Paint search bubble to shows the matched records.
   * @private 
   * @jsxobf-clobber 
   */  
  Search_prototype._paintSearchBubble = function() {
    if (!this._search_bubble){
      var bubble = this._search_bubble = new jsx3.gui.Menu('search_bubble');
      bubble.setDisplay(jsx3.gui.Block.DISPLAYNONE);
    }
    return this._search_bubble;
  };

  Search_prototype.getFileList = function() {
    return this._file_list;
  };

  Search_prototype.getSearchBox = function() {
    return this._search_box;
  };

  Search_prototype.getBubble = function() {
    return this._search_bubble;
  };

  /** @private @jsxobf-clobber */
  Search_prototype._resetSearchBox = function() {
    Search.Logger.trace('_resetSearchBox');
    var txt = this._search_box;
    if (txt.getValue() != '' && txt.getColor() != '#777777') {
      txt.setBackgroundColor('#F6F6F6', true).setColor('#777777', true);
      Search.Logger.trace('_resetSearchBox: has been reset.');
    }
  };

  /** @private @jsxobf-clobber */
  Search_prototype._doKeyDown = function(objGUI, objEvent) {
    Search.Logger.trace('_doKeyDown: this: ' + this + ', search_box: ' + objGUI + ', srcElement: ' + objEvent.srcElement() + ', objEvent: ' + objEvent.keyCode());
    this._previousSearchValue = objGUI.getValue();
    /* Alt + F3 to search down */
    if (/* objEvent.altKey() &&  */objEvent.keyCode() == (Event.KEY_F1 + 2)/*F3*/)
      this.publish({subject: Search.ON_SEARCH, keyword: objGUI.getValue(), up: false, hold: true/* hold focus*/});
    /* Alt + F2 to search up (have a problem that index not correct when turned from search down) */
    if (/* objEvent.altKey() &&  */objEvent.keyCode() == (Event.KEY_F1 + 1)/*F2*/)
      this.publish({subject: Search.ON_SEARCH, keyword: objGUI.getValue(), up: true, hold: true/* hold focus*/});
    /* Create a bubble list to show matched records */
    if (objEvent.downArrow())
      this.publish({subject: Search.ON_SEARCH, keyword: objGUI.getValue(), index: 'NAN', objEVENT: objEvent, bubble: true});
  };

  /** @private @jsxobf-clobber */
  Search_prototype._doKeyUp = function(objGUI, objEvent) {
    Search.Logger.trace('_doKeyUp: this: ' + this + ', search_box: ' + objGUI);
    if (!objEvent.isArrowKey() && !objEvent.isFunctionKey() && (objGUI.getValue() != this._previousSearchValue)) {
      this._resetSearchBox();
    }
  };

  /** @private @jsxobf-clobber */
  Search_prototype._doChange = function(objGUI, objEvent) {
    Search.Logger.trace('_doChange: this: ' + this + ', search_box: ' + objGUI);
  };

  /** @private @jsxobf-clobber */
  Search_prototype._doExecute = function(objGUI, objEvent) {
    Search.Logger.trace('_doExecute: this: ' + this + ', search_box: ' + objGUI + ', this.publish: ' + !!this.publish);
    var file_list = this._file_list;
    var selectedIds = file_list.getSelectedIds();
    var nodes = file_list.getSelectedNodes();
    var _callback = function() {
      file_list.doEvent(Interactive.EXECUTE, {objEVENT: objEvent, strRECORDID: selectedIds[0] ,strRECORDIDS: selectedIds});
    };
    if (nodes.size() > 0 && nodes.get(0).getAttribute('name').indexOf(objGUI.getValue()) >= 0)
      _callback.call(this);
    else
      this.publish({subject: Search.ON_SEARCH, keyword: objGUI.getValue(), objEVENT: objEvent, hold: true, callback: _callback});
    /* reset search result list */
    try{this.getBubble().clearXmlData();}catch(e){}
  };

  /** @private @jsxobf-clobber */
  Search_prototype._doClick = function(objGUI, objEvent) {
    Search.Logger.trace('_doClick: this: ' + this + ', objGUI: ' + objGUI);
  };

  /** @private @jsxobf-clobber */
  Search_prototype._doMouseDown = function(objGUI, objEvent) {
    Search.Logger.trace('_doMouseDown: this: ' + this + ', search_box: ' + objGUI);
    if (objGUI.getValue() == Search.DEFAULT_VALUE)
      objGUI.setValue('', true);
    // if (objEvent.rightButton())
      // this.publish({subject: Search.ON_SEARCH, keyword: objGUI.getValue(), index: 'NAN', objEVENT: objEvent, hold: false});
  };

  /** @private @jsxobf-clobber */
  Search_prototype._doMouseUp = function(objGUI, objEvent) {
    Search.Logger.trace('_doMouseUp: this: ' + this + ', objGUI: ' + objGUI);
  };

  /** @private @jsxobf-clobber */
  Search_prototype._doBlur = function(objGUI, objEvent) {
    var strText = objGUI.getValue();
    if (!strText || (strText.trim() == '')) 
      objGUI.setValue(Search.DEFAULT_VALUE, true);
    Search.Logger.trace('_doBlur: this: ' + this + ' objGUI: ' + objGUI + ', strText: ' + strText);
  };

  /** @private @jsxobf-clobber */
  Search_prototype._checkBubble = function(objGUI, objEvent) {
    Search.Logger.trace('_checkBubble: this: ' + this + ', objGUI: ' + objGUI + ', jsxmenu: ' + (this.getBubble().getRecordIds().length>0));
    return this.getBubble().getRecordIds().length>0;
  };

});

/**
 * Search engine.
 */
jsx3.Class.defineClass("jsx3.extension.browser.SearchEngine", null, [jsx3.util.EventDispatcher], function(Search, Search_prototype) {

  Search.Logger = jsx3.util.Logger.getLogger(Search.jsxclass.getName());

  Search.MENU_CACHE_ID = "jsx3.extension.browser.SearchEngine.CACHE_MATCHED_LIST";
  Search.TEMPLATE = null;

  Search.launch = function(searchInstance) {
    Search.Logger.trace('launch search engine...');
    var instance = new Search(searchInstance);

    var resource = instance.getSearchInstance().getPlugIn().getResource('search_xsl');
    if (!resource)
      throw new jsx3.Exception('Error occurred while load XSL template resource "search_xsl".');

    resource.load().when(function(){
      Search.TEMPLATE = new jsx3.xml.Template(resource.getData());
    });

    return instance;
  };

  /**
   * Position of the record in the CDF document to be returned
   */
  Search_prototype.index = 1;

  Search_prototype.init = function(searchInstance) {
    this._searchInstance = searchInstance;
    this.searchbox = this.getSearchInstance().getSearchBox();
    this.filelist = this.getSearchInstance().getFileList();
    this.bubble = this.getSearchInstance().getBubble();

    this.bubble.setXMLId(Search.MENU_CACHE_ID);
    this.bubble.subscribe(jsx3.gui.Interactive.EXECUTE, this, jsx3.$F(function(objEvent){
      Search.Logger.trace('bubble executed. objEvent: ' + jsx3.$O.json(objEvent, false));
      this.filelist.selectRecord(objEvent.context.strRECORDID);
      this.getSearchInstance()._doExecute(this.searchbox, objEvent.context.objEVENT);
      // this.filelist.doEvent(jsx3.gui.Interactive.EXECUTE, {/* objEVENT: objEvent.context.objEVENT, strRECORDID: objEvent.context.strRECORDID, strRECORDIDS: [objEvent.context.strRECORDID] */});
    }).bind(this));
    this.bubble.subscribe(jsx3.gui.Interactive.HIDE, this, jsx3.$F(function(objEvent){
      jsx3.sleep(function(){this.searchbox.focus();}, null, this);
    }).bind(this));

    searchInstance.subscribe(jsx3.extension.browser.Search.ON_SEARCH, this, this.doSearch);
  };

 /**
  * Search and focuses the file or folder found in list. 
  * @param objEvent {Object} parameters for search required.
  */
  Search_prototype.doSearch = function(objEvent) {
    Search.Logger.trace('doSearch: objEvent:{subject: ' + objEvent.subject + ', keyword: ' + objEvent.keyword + '}');
    var strKeyword = objEvent.keyword;
    if (!Search.TEMPLATE || !strKeyword || strKeyword.trim() == '') return;

    var list = this.filelist, bubble = this.bubble, txt = this.searchbox;

    var params = {'keyword': strKeyword, 'index': (objEvent.index ? objEvent.index : this.index)};
    Search.TEMPLATE.setParams(params);
    var xml = Search.TEMPLATE.transform(list.getXML());
    var strResult = this._getWrappedRecordID(xml);

    if (strResult.indexOf('SEARCHFILELIST_FOR_MENU:') == 0) {
      var strRecords = strResult.replace('SEARCHFILELIST_FOR_MENU:', '').replace(/name=/g,'jsxtext=');
      var cdf = jsx3.xml.CDF.newDocument();
      cdf.loadXML('<data jsxid="jsxroot">' + strRecords + '</data>');
      bubble.getServer().getCache().setDocument(Search.MENU_CACHE_ID, cdf);
      if (objEvent.bubble) {
        var objOffset = txt.getAbsolutePosition();
        objOffset.L = objOffset.L - 10;
        objOffset.T = objOffset.T + 8;
        bubble.showContextMenu(objEvent.objEVENT, txt.getParent(), null, objOffset); 
      }
    } else if (strResult == 'SEARCHFILELIST_NO_MATCHED') {
      txt.setBackgroundColor('#F66F6F', true).setColor('#FEFEFE', true);
      txt.getRendered().select();
    } else if (strResult.indexOf('SEARCHFILELIST_OVERHANG') == 0) {
      if (objEvent.up)
        this.index = parseInt(strResult.split(':')[1]);
      else
        this.index = 1;
      this.doSearch(objEvent);
    } else if (list.getRecord(strResult)) {
      if (objEvent.up)
        this.index--;
      else
        this.index++;
      list.focusRowById(strResult);
    }

    if (objEvent.callback)
      jsx3.sleep(jsx3.$F(function(){objEvent.callback.call(this.getSearchInstance());}).bind(this));
    if (objEvent.hold) {
      jsx3.sleep(function(){
        try{
          txt.focus();
          txt.getRendered().select();
        }catch(e){}
      });
    }
  };

  Search_prototype.getSearchInstance = function() {
    return this._searchInstance;
  };

  Search_prototype._getWrappedRecordID = function(xml) {
    if (xml.indexOf('<SEARCHFILELIST_SEARCH_RECORDID_WRAPPER>') > -1) {
      return xml.substring(xml.indexOf(">") + 1, xml.lastIndexOf("<"));
    } else {
      return !(/\<SEARCHFILELIST_SEARCH_RECORDID_WRAPPER(.*)\/\>/gi.test(xml)) || '';
    }
  };

});
